package version4.Client.serviceCenter.balance.impl;

import version4.Client.serviceCenter.balance.LoadBalance;

import java.util.*;

public class ConsistencyHashBalance implements LoadBalance {
    // 虚拟节点数量，足够多的虚拟节点配合hash算法，使得服务器均匀分布在hash环上
    private static final int VIRTUAL_NODE_NUM = 500;
    // 虚拟节点分配，key为虚拟节点的hash值，value为虚拟节点的地址
    private SortedMap<Integer, String> virtualNodeMap = new TreeMap<>();
    // 真实节点地址列表
    private List<String> realNodeList = new LinkedList<>();
    // 模拟初始服务器
    private String[] servers = null;

    private void init(List<String> serverList) {
        for(String server : serverList) {
            // 如果服务器列表发生变化，需要重新初始化
            // 新增服务地址
            if(!realNodeList.contains(server) && serverList.contains(server)) {
                addAddress(server);
            }
            // 删除服务地址
            if(realNodeList.contains(server) && !serverList.contains(server)) {
                removeAddress(server);
            }

        }
    }

    // 我们只将虚拟节点部署在hash环上，不部署真实节点
    private String getServer(String node,List<String> servers) {
        init(servers);
        int hashCode = getHash(node);
        Integer key = null;
        // 找到大于等于该hashCode的所有服务器地址组成的Map
        SortedMap<Integer, String> tailMap = virtualNodeMap.tailMap(hashCode);
        if(tailMap.isEmpty()) {
            // 没有大于等于该hashCode的服务器地址，返回第一个服务器地址
            key = virtualNodeMap.firstKey();
        } else {
            // 有大于等于该hashCode的服务器地址，返回第一个服务器地址
            key = tailMap.firstKey();
        }
        String virtualNode = virtualNodeMap.get(key);
        System.out.println("请求被路由到虚拟节点 ["+virtualNode+"]");
        return virtualNode.substring(0,virtualNode.indexOf("&&VN"));
    }

    private static int getHash(String str) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < str.length(); i++)
            hash = (hash ^ str.charAt(i)) * p;
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        // 如果算出来的值为负数则取其绝对值
        if (hash < 0)
            hash = Math.abs(hash);
        return hash;
    }

    @Override
    public String balance(List<String> addressList) {
        String random = UUID.randomUUID().toString();
        return getServer(random,addressList);
    }

    @Override
    public void addAddress(String address) {
        if(!realNodeList.contains(address)) {
            // 添加真实节点
            realNodeList.add(address);
            System.out.println("真实节点 ["+address+"] 加入");
            for(int i = 0; i < VIRTUAL_NODE_NUM; i++) {
                // 添加虚拟节点
                String virtualNode = address + "&&VN" + i;
                int hashCode = getHash(virtualNode);
                virtualNodeMap.put(hashCode, virtualNode);
                System.out.println("虚拟节点 ["+virtualNode+"] 加入, hash值为 "+hashCode);
            }
        }
    }

    @Override
    public void removeAddress(String address) {
        if(realNodeList.contains(address)) {
            // 删除真实节点
            realNodeList.remove(address);
            System.out.println("真实节点 ["+address+"] 删除");
            for(int i = 0; i < VIRTUAL_NODE_NUM; i++) {
                // 删除虚拟节点
                String virtualNode = address + "&&VN" + i;
                int hashCode = getHash(virtualNode);
                virtualNodeMap.remove(hashCode);
                System.out.println("虚拟节点 ["+virtualNode+"] 删除");
            }
        }
    }
}
